#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <fstream>
#include <cstdint>
#include "util.hpp"

namespace ns_index
{
	struct DocInfo
	{
		std::string title;
		std::string content;
		std::string url;
		uint64_t doc_id;
	};
	struct InvertedElem
	{
		uint64_t doc_id;
		std::string word;
		int weight;
		InvertedElem() : weight(0) {}
	};

	// 倒排拉链
	typedef std::vector<InvertedElem> InvertedList;
	class Index
	{
	private:
		std::vector<DocInfo> forward_index;
		std::unordered_map<std::string, InvertedList> inverted_index;

		static Index *instance;
		static std::mutex mtx;

	private:
		Index() {}
		Index(const Index &) = delete;
		Index &operator=(const Index &) = delete;

	public:
		~Index() {}

		static Index *GetInstance()
		{
			if (nullptr == instance)
			{
				mtx.lock();
				if (nullptr == instance)
				{
					instance = new Index();
				}
				mtx.unlock();
			}
			return instance;
		}

		// 正排：通过文档id找到文档
		DocInfo *GetForwardIndex(uint64_t doc_id)
		{
			if (doc_id >= forward_index.size())
			{
				std::cerr << "ForwardIndex doc_id:" << doc_id << "out of index" << std::endl;
				return nullptr;
			}
			return &forward_index[doc_id];
		}
		// 倒排：通过关键字找到倒排拉链
		InvertedList *GetInvertedList(const std::string &word)
		{
			auto iter = inverted_index.find(word);
			if (iter == inverted_index.end())
			{
				std::cerr << word << " have no InvertedList" << std::endl;
				return nullptr;
			}
			return &(iter->second);
		}

		// 构建索引,将整合后的文件内容交给类管理
		bool BuildIndex(const std::string &input)
		{
			std::ifstream in(input, std::ios::in | std::ios::binary);
			if (!in.is_open())
			{
				std::cerr << "open: " << input << "failed" << std::endl;
				return false;
			}
			std::string line;
			int cnt = 0;
			while (std::getline(in, line))
			{
				DocInfo *doc = BuildForwardIndex(line);
				if (doc == nullptr)
				{
					std::cerr << "build forward index failed" << std::endl;
					continue;
				}
				bool res = BuildInvertedIndex(*doc);
				if (!res)
				{
					std::cerr << "build inverted index failed" << std::endl;
				}
				cnt++;
				if (cnt % 50 == 0) std::cout << "索引已建立:" << cnt << "个" << std::endl;
				if (cnt == 4700) return true;
			}
			return true;
		}

	private:
		DocInfo *BuildForwardIndex(const std::string &line)
		{
			// 分割行
			std::vector<std::string> res;
			const std::string sep = "\3";
			ns_util::StringUtil::Split(line, &res, sep);
			if (res.size() != 3)
			{
				return nullptr;
			}
			// 填写DocInfo
			DocInfo doc;
			doc.title = res[0];
			doc.content = res[1];
			doc.url = res[2];
			doc.doc_id = forward_index.size();

			forward_index.push_back(std::move(doc));
			return &forward_index.back();
		}
		bool BuildInvertedIndex(DocInfo &doc)
		{
			struct word_cnt
			{
				int title_cnt;
				int content_cnt;
				word_cnt() : title_cnt(0), content_cnt(0)
				{}
			};
			std::unordered_map<std::string, word_cnt> word_map; // 用来暂存词频的映射表

			// 1.对标题进行分词
			std::vector<std::string> title_words;
			ns_util::JiebaUtil::CutString(doc.title, &title_words);
			for (auto word : title_words)
			{
				boost::to_lower(word);
				word_map[word].title_cnt++;
			}

			// 2.对内容进行分词
			std::vector<std::string> content_words;
			ns_util::JiebaUtil::CutString(doc.content, &content_words);
			for (auto word : content_words)
			{
				boost::to_lower(word);
				word_map[word].content_cnt++;
			}
#define X 10
#define Y 1
			for (auto &item : word_map)
			{
				InvertedElem elem;
				elem.doc_id = doc.doc_id;
				elem.word = item.first;
				elem.weight = X * item.second.title_cnt + Y * item.second.content_cnt;
				InvertedList &invertedlist = inverted_index[item.first];
				invertedlist.push_back(std::move(elem));
			}
			return true;
		}
	};
	Index *Index::instance = nullptr;
	std::mutex Index::mtx;
};